﻿
namespace Aeon.Emulator.Keyboard
{
    /// <summary>
    /// Contains methods for converting between scan codes and keyboard codes.
    /// </summary>
    internal static class ScanCodeConverter
    {
        #region Public Static Methods
        /// <summary>
        /// Generates a type-ahead keyboard code from a scan code and the key modifier state.
        /// </summary>
        /// <param name="lowScan">Low byte of the scan code.</param>
        /// <param name="modifiers">Current state of keyboard modifier keys.</param>
        /// <returns>Type-ahead keyboard code.</returns>
        public static ushort ConvertToKeyboardCode(byte lowScan, KeyModifiers modifiers)
        {
            if(lowScan >= codeMap.Length / 8)
                return 0;

            if((modifiers & KeyModifiers.Alt) == KeyModifiers.Alt)
                return codeMap[lowScan * 8 + 3];
            else if((modifiers & KeyModifiers.Ctrl) == KeyModifiers.Ctrl)
                return codeMap[lowScan * 8 + 2];
            else if((modifiers & shiftCaps) == shiftCaps)
                return codeMap[lowScan * 8 + 6];
            else if((modifiers & shiftNum) == shiftNum)
                return codeMap[lowScan * 8 + 7];
            else if((modifiers & KeyModifiers.Shift) != 0)
                return codeMap[lowScan * 8 + 1];
            else if((modifiers & KeyModifiers.NumLock) != 0)
                return codeMap[lowScan * 8 + 4];
            else if((modifiers & KeyModifiers.CapsLock) != 0)
                return codeMap[lowScan * 8 + 5];
            else
                return codeMap[lowScan * 8];
        }
        #endregion

        #region Private Constants
        private const KeyModifiers shiftCaps = KeyModifiers.Shift | KeyModifiers.CapsLock;
        private const KeyModifiers shiftNum = KeyModifiers.Shift | KeyModifiers.NumLock;
        #endregion

        #region Private Static Fields
        private static readonly ushort[] codeMap =
        {
            //norm  shft    ctrl    alt     num     caps    shcap   shnum
            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
            0x011b, 0x011b, 0x011b, 0x011b, 0x011b, 0x011b, 0x011b, 0x011b, //ESC
            0x0231, 0x0321, 0x0000, 0x7800, 0x0231, 0x0231, 0x0231, 0x0321, //1 !
            0x0332, 0x0340, 0x0300, 0x7900, 0x0332, 0x0332, 0x0332, 0x0332, //2 @
            0x0433, 0x0423, 0x0000, 0x7a00, 0x0433, 0x0433, 0x0423, 0x0423, //3 #
            0x0534, 0x0524, 0x0000, 0x7b00, 0x0534, 0x0534, 0x0524, 0x0524, //4 $
            0x0635, 0x0625, 0x0000, 0x7c00, 0x0635, 0x0635, 0x0625, 0x0625, //5 %
            0x0736, 0x075e, 0x071e, 0x7d00, 0x0736, 0x0736, 0x075e, 0x075e, //6 ^

            0x0837, 0x0826, 0x0000, 0x7e00, 0x0837, 0x0837, 0x0826, 0x0826, //7 &
            0x0938, 0x092a, 0x0000, 0x7f00, 0x0938, 0x0938, 0x092a, 0x092a, //8 *
            0x0a39, 0x0a28, 0x0000, 0x8000, 0x0a39, 0x0a39, 0x0a28, 0x0a28, //9 (
            0x0b30, 0x0b29, 0x0000, 0x8100, 0x0b30, 0x0b30, 0x0b29, 0x0b29, //0 )
            0x0c2d, 0x0c5f, 0x0000, 0x8200, 0x0c2d, 0x0c2d, 0x0c5f, 0x0c5f, //- _
            0x0d3d, 0x0d2b, 0x0000, 0x8300, 0x0d3d, 0x0d3d, 0x0d2b, 0x0d2b, //= +
            0x0e08, 0x0e08, 0x0e7f, 0x0000, 0x0e08, 0x0e08, 0x0e08, 0x0e08, //bksp
            0x0f09, 0x0f00, 0x0000, 0x0000, 0x0f09, 0x0f09, 0x0f00, 0x0f00, //Tab

            0x1071, 0x1051, 0x1011, 0x1000, 0x1071, 0x1051, 0x1051, 0x1071, //Q
            0x1177, 0x1057, 0x1017, 0x1100, 0x1077, 0x1057, 0x1057, 0x1077, //W
            0x1265, 0x1245, 0x1205, 0x1200, 0x1265, 0x1245, 0x1245, 0x1265, //E
            0x1372, 0x1352, 0x1312, 0x1300, 0x1272, 0x1252, 0x1252, 0x1272, //R
            0x1474, 0x1454, 0x1414, 0x1400, 0x1474, 0x1454, 0x1454, 0x1474, //T
            0x1579, 0x1559, 0x1519, 0x1500, 0x1579, 0x1559, 0x1579, 0x1559, //Y
            0x1675, 0x1655, 0x1615, 0x1600, 0x1675, 0x1655, 0x1675, 0x1655, //U
            0x1769, 0x1749, 0x1709, 0x1700, 0x1769, 0x1749, 0x1769, 0x1749, //I

            0x186f, 0x184f, 0x180f, 0x1800, 0x186f, 0x184f, 0x186f, 0x184f, //O
            0x1970, 0x1950, 0x1910, 0x1900, 0x1970, 0x1950, 0x1970, 0x1950, //P
            0x1a5b, 0x1a7b, 0x1a1b, 0x0000, 0x1a5b, 0x1a5b, 0x1a7b, 0x1a7b, //[ {
            0x1b5d, 0x1b7d, 0x1b1d, 0x0000, 0x1b5d, 0x1b5d, 0x1b7d, 0x1b7d, //] }
            0x1c0d, 0x1c0d, 0x1c0a, 0x0000, 0x1c0d, 0x1c0d, 0x1c0a, 0x1c0a, //enter
            0x1d00, 0x1d00, 0x1d00, 0x1d00, 0x1d00, 0x1d00, 0x1d00, 0x1d00, //ctrl
            0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x1e61, 0x1e41, 0x1e61, 0x1e41, //A
            0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x1f73, 0x1f53, 0x1f73, 0x1f53, //S

            0x2064, 0x2044, 0x2004, 0x2000, 0x2064, 0x2044, 0x2064, 0x2044, //D
            0x2166, 0x2146, 0x2106, 0x2100, 0x2166, 0x2146, 0x2166, 0x2146, //F
            0x2267, 0x2247, 0x2207, 0x2200, 0x2267, 0x2247, 0x2267, 0x2247, //G
            0x2368, 0x2348, 0x2308, 0x2300, 0x2368, 0x2348, 0x2368, 0x2348, //H
            0x246a, 0x244a, 0x240a, 0x2400, 0x246a, 0x244a, 0x246a, 0x244a, //J
            0x256b, 0x254b, 0x250b, 0x2500, 0x256b, 0x254b, 0x256b, 0x254b, //K
            0x266c, 0x264c, 0x260c, 0x2600, 0x266c, 0x264c, 0x266c, 0x264c, //L
            0x273b, 0x273a, 0x0000, 0x0000, 0x273b, 0x273b, 0x273a, 0x273a, //; :

            0x2827, 0x2822, 0x0000, 0x0000, 0x2827, 0x2827, 0x2822, 0x2822, //' "
            0x2960, 0x297e, 0x0000, 0x0000, 0x2960, 0x2960, 0x297e, 0x297e, //` ~
            0x2a00, 0x2a00, 0x2a00, 0x2a00, 0x2a00, 0x2a00, 0x2a00, 0x2a00, //lshf
            0x2b5c, 0x2b7c, 0x2b1c, 0x0000, 0x2b5c, 0x2b5c, 0x2b7c, 0x2b7c, //\ |
            0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x2c7a, 0x2c5a, 0x2c7a, 0x2c5a, //Z
            0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x2d78, 0x2d58, 0x2d78, 0x2d58, //X
            0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x2e63, 0x2e43, 0x2e63, 0x2e43, //C
            0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x2f76, 0x2f56, 0x2f76, 0x2f56, //V

            0x3062, 0x3042, 0x3002, 0x3000, 0x3062, 0x3042, 0x3062, 0x3042, //B
            0x316e, 0x314e, 0x310e, 0x3100, 0x316e, 0x314e, 0x316e, 0x314e, //N
            0x326d, 0x324d, 0x320d, 0x3200, 0x326d, 0x324d, 0x326d, 0x324d, //M
            0x332c, 0x333c, 0x0000, 0x0000, 0x332c, 0x332c, 0x333c, 0x333c, //, <
            0x342e, 0x343e, 0x0000, 0x0000, 0x342e, 0x342e, 0x343e, 0x343e, //. >
            0x352f, 0x353f, 0x0000, 0x0000, 0x352f, 0x352f, 0x353f, 0x353f, // / ?
            0x3600, 0x3600, 0x3600, 0x3600, 0x3600, 0x3600, 0x3600, 0x3600, //rshf
            0x372a, 0x0000, 0x3710, 0x0000, 0x372a, 0x372a, 0x0000, 0x0000, //* PS

            0x3800, 0x3800, 0x3800, 0x3800, 0x3800, 0x3800, 0x3800, 0x3800, //alt
            0x3920, 0x3920, 0x3920, 0x0000, 0x3920, 0x3920, 0x3920, 0x3920, //spc
            0x3a00, 0x3a00, 0x3a00, 0x3a00, 0x3a00, 0x3a00, 0x3a00, 0x3a00, //caps
            0x3b00, 0x5400, 0x5e00, 0x6800, 0x3b00, 0x3b00, 0x5400, 0x5400, //F1
            0x3c00, 0x5500, 0x5f00, 0x6900, 0x3c00, 0x3c00, 0x5500, 0x5500, //F2
            0x3d00, 0x5600, 0x6000, 0x6a00, 0x3d00, 0x3d00, 0x5600, 0x5600, //F3
            0x3e00, 0x5700, 0x6100, 0x6b00, 0x3e00, 0x3e00, 0x5700, 0x5700, //F4
            0x3f00, 0x5800, 0x6200, 0x6c00, 0x3f00, 0x3f00, 0x5800, 0x5800, //F5

            0x4000, 0x5900, 0x6300, 0x6d00, 0x4000, 0x4000, 0x5900, 0x5900, //F6
            0x4100, 0x5a00, 0x6400, 0x6e00, 0x4100, 0x4100, 0x5a00, 0x5a00, //F7
            0x4200, 0x5b00, 0x6500, 0x6f00, 0x4200, 0x4200, 0x5b00, 0x5b00, //F8
            0x4300, 0x5c00, 0x6600, 0x7000, 0x4300, 0x4300, 0x5c00, 0x5c00, //F9
            0x4400, 0x5d00, 0x6700, 0x7100, 0x4400, 0x4400, 0x5d00, 0x5d00, //F10
            0x4500, 0x4500, 0x4500, 0x4500, 0x4500, 0x4500, 0x4500, 0x4500, //num
            0x4600, 0x4600, 0x4600, 0x4600, 0x4600, 0x4600, 0x4600, 0x4600, //scrl
            0x4700, 0x4737, 0x7700, 0x0000, 0x4737, 0x4700, 0x4737, 0x4700, //home

            0x4800, 0x4838, 0x0000, 0x0000, 0x4838, 0x4800, 0x4838, 0x4800, //up
            0x4900, 0x4939, 0x8400, 0x0000, 0x4939, 0x4900, 0x4939, 0x4900, //pgup
            0x4a2d, 0x4a2d, 0x0000, 0x0000, 0x4a2d, 0x4a2d, 0x4a2d, 0x4a2d, //-
            0x4b00, 0x4b34, 0x7300, 0x0000, 0x4b34, 0x4b00, 0x4b34, 0x4b00, //left
            0x4c00, 0x4c35, 0x0000, 0x0000, 0x4c35, 0x4c00, 0x4c35, 0x4c00, //Center
            0x4d00, 0x4d36, 0x7400, 0x0000, 0x4d36, 0x4d00, 0x4d36, 0x4d00, //right
            0x4e2b, 0x4e2b, 0x0000, 0x0000, 0x4e2b, 0x4e2b, 0x4e2b, 0x4e2b, //+
            0x4f00, 0x4f31, 0x7500, 0x0000, 0x4f31, 0x4f00, 0x4f31, 0x4f00, //end

            0x5000, 0x5032, 0x0000, 0x0000, 0x5032, 0x5000, 0x5032, 0x5000, //down
            0x5100, 0x5133, 0x7600, 0x0000, 0x5133, 0x5100, 0x5133, 0x5100, //pgdn
            0x5200, 0x5230, 0x0000, 0x0000, 0x5230, 0x5200, 0x5230, 0x5200, //ins
            0x5300, 0x532e, 0x0000, 0x0000, 0x532e, 0x5300, 0x532e, 0x5300, //del
            
            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // --
            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // --
            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // --

            0x5700, 0x0000, 0x0000, 0x0000, 0x5700, 0x5700, 0x0000, 0x0000, //F11
            0x5800, 0x0000, 0x0000, 0x0000, 0x5800, 0x5800, 0x0000, 0x0000 //F12
        };
        #endregion
    }
}
